<template>
  <div
    v-loading="isLoading"
    class="chartMain"
    :class="componentStyleClass"
  >
    <div
      :id="'chartMain' + chartId"
      class="chartMain-inner"
      style="width:100%;height: 100%;"
    >
      <Tooltip
        ref="Tooltip"
        :component-style="config.config.property.componentStyle"
        type="echart"
        :tooltip-params="tooltipParams"
      />
      <ComponentTitle
        :property="config.config.property"
        :title-text-style="getCssStyle('title')"
      />
      <chart
        ref="dataChart"
        v-visual-tooltip="getTooltip()"
        :w="w"
        :h="h"
        :is-show="isShow"
        :is-loading="isLoading"
        :is-no-data="isNoData"
        :config="config"
        :widget-instance-id="chartId"
        @chart-click="chartClick"
      ></chart>
    </div>
    <div
      v-if="isNoData"
      class="countlessData_vChart"
    >
      <p :style="getCssStyle({fontSize:14})">{{ $demoi18n('message.noData') }}</p>
    </div>
  </div>
</template>
<script>
import chartBase from '@/mixin/chart-base.js';
import emitComponentBase from '@/mixin/emit-component-base.js';
import dataModel from '@/models/panel-config-data.js';
import { EMIT_PARAMS_OBJ } from '@/utils/constant';
import { getAggFnList, isEmpty } from '@/utils/util';
import ComponentTitle from '../common/component-title/component-title.vue';
import Tooltip from '../common/tooltip/tooltip.vue';
import Chart from './chart';
import { convertData, getXAxisKeyAndSeriesKey } from './chart-option';
import config from './config/config';

const XMINER = 'xMiner';
const XTAG = 'xTag';

// 转换表单数据
const convertFormData = function(tableData) {
  const form = {};
  for (const key in tableData) {
    if (tableData[key].mapping) {
      let { field } = tableData[key];
      if (tableData[key].field.indexOf('y') > -1) {
        field = 'y';
      }
      form[field] = tableData[key].mapping;
    }
  }
  return form;
};

export default {
  name: 'CommonXMultipleY',
  components: {
    Chart,
    ComponentTitle,
    Tooltip,
  },
  mixins: [chartBase, emitComponentBase],
  props: ['pareentId', 'widgetId', 'widgetInstanceId', 'widgetName', 'identifier', 'layout', 'w', 'h'],

  data() {
    return {
      isLoading: false,
      isNoData: false,
      orgConfig: [],
      isShow: true,
      chartId: '',
      config: null,
      datas: [],
      params: [],
      apiCompletedCount: 0,
      tooltipParams: {},
    };
  },
  computed: {},
  watch: {
    apiCompletedCount(count, oldCount) {
      if (Object.keys(this.config.config.data.datas).length === count) {
        this.isLoading = false;
        const convertedData = this.convertDatas();
        this.convertedData = convertedData;

        // 判断有无数据
        if (convertedData) {
          this.isNoData = false;
        } else {
          this.isNoData = true;
        }

        // 拿着数据和配置画图去了
        this.$refs.dataChart.generateChart(convertedData, this.config.config.property, this.config.config.data);
      }
    },
  },
  created() {
    this.orgConfig = config;
    this.config = JSON.parse(JSON.stringify(this.orgConfig));

    this.chartId = `${this.widgetInstanceId}_01`;
  },
  mounted() {
    if (window.isLocalEnv) {
      this.loadWidget();
    }
  },
  methods: {

    // 鼠标经过事件绑定
    mouseoverInit() {
      setTimeout(() => {
        const { chartObj } = this.$refs.dataChart;
        Object.entries(chartObj).map(([i]) => {
          const chart = chartObj[i];
          const tooltipVm = this.$refs.Tooltip;
          if (chart && tooltipVm) {
            chart.on('mouseover', params => {
              tooltipVm.chartSerieItem = params;
            });
            chart.on('mouseout', params => {
              tooltipVm.chartSerieItem = params;
            });
          }
        });
      }, 1000);
    },

    // 初始加载时 以及 编排面更改配置时 触发
    loadWidget(pageStatus) {
      this.pageStatus = pageStatus;
      this.updateConfigProperty();

      if (window.isLocalEnv || this.emitComponentStatus) {
        this.updateParams(pageStatus);
        this.debounceGetData();
      }
      this.afterLoadWidget();
    },

    // api请求获取图表数据  --图表之间的convertData一般都不相同
    getChartData() {
      const { apis, datas } = this.config.config.data;

      if (datas && datas.length) {
        this.apiCompletedCount = 0;

        // 补充公共参数
        const selfVm = this;
        const matchParams = this._getCommonParamsByEvents(this.config.events);

        datas.forEach((dataItem, dataIndex) => {
          const apiCode = dataItem[0].mapping;
          const apiObj = apis.find(a => String(a.apiCode) === String(apiCode));

          // 表格数据去掉第一列（api）
          const tableData = JSON.parse(JSON.stringify(dataItem));
          tableData.shift();

          // 结合查询条件实时生成对应的params
          let params = this.getParams({
            dataItem: tableData,
            dataIndex,
            apiObj,
          });
          if (!apiObj.dataSourceType) {
            return;
          }
          const method = `getChartData${apiObj.dataSourceType}`;

          // 补充公共参数
          if (!params) {
            params = {};
          }
          if (apiObj.dataSourceType === 'xMiner' || apiObj.dataSourceType === 'xMinerPublicDatabase') {
            params.params = selfVm._addCommonParamsBySourceType('xMiner', params.params, matchParams);
          } else {
            selfVm._addCommonParamsBySourceType('xTag', params, matchParams);
          }
          this.tooltipParams = params;

          params = this.getCorrectParams(params, apiObj.dataSourceType);
          dataModel[method](params, apiObj)
            .then(result => {
              if (dataIndex === 0) {
                this.sendDataToConfig(result);
              }
              this.datas[dataIndex] = result;
              this.apiCompletedCount++;
            })
            .finally(() => {
              this.isLoading = false;
            });
        });
      }
    },

    // 转换数据
    convertDatas() {
      const convertedData = [];

      // 拿到公用x轴的数据
      const dataX = this.getDataCommonX();
      const { xAxisKey } = getXAxisKeyAndSeriesKey(dataX);
      let hasData = false;
      this.datas.forEach((dataItem, dataIndex) => {
        const { seriesKey } = getXAxisKeyAndSeriesKey(dataItem);
        const { chartType } = this.config.config.property.data[dataIndex];
        convertedData[dataIndex] = convertData({
          data: dataItem,
          xAxisKey,
          seriesKey,
          chartType,
        });
        if (convertedData[dataIndex]) {
          hasData = true;
        }
      });
      if (hasData) {
        return convertedData;
      } else {
        return null;
      }
    },

    // 拿到公用x轴的数据
    getDataCommonX() {
      let dataX = this.datas[0];
      const { datas } = this.config.config.data;
      datas.forEach((dataItem, dataIndex) => {
        const itemX = dataItem.find(d => String(d.field) === 'x');
        if (itemX.isCheck) {
          dataX = this.datas[dataIndex];
        }
      });
      return dataX;
    },

    /* 重写mixin的方法 */
    // 获取配置页面的参数  事件接收的参数不变
    getParams({ dataItem, dataIndex, apiObj }) {
      let params = null;
      if (!apiObj.dataSourceType) {
        return params;
      }

      if (apiObj.dataSourceType === XMINER) {
        params = {
          ...this.params[0],
          apiCode: apiObj.apiCode, // 配置页面中选择的api编码
          propertyMapping: convertFormData(dataItem), // 配置页面中 选择的呈现字段
        };
      } else if (apiObj.dataSourceType === XTAG) {
        params = this.getParamsXTag({ dataItem, dataIndex, apiObj });
      } else {}
      return params;
    },
    getParamsXTag({ dataItem, dataIndex, apiObj }) {
      const params = {
        ...this.params[0],
        propertyMapping: convertFormData(dataItem), // 配置页面中 选择的呈现字段
      };
      const aggFnList = getAggFnList(); // 聚合函数列表

      // 初始化配置
      let isFirstAgg = true;
      let isFirstCustomFields = true;
      delete params.agg;
      delete params.customFields;

      dataItem.forEach(item => {
        let hasFn = false;
        let fnName = '';

        // 判断是否自定义计算字段
        if (item.type) {
          // 是否包含聚合函数字段
          aggFnList.forEach(name => {
            if (!hasFn && item.mapping.includes(`${name}(`)) {
              hasFn = true;
              fnName = name;
            }
          });

          // 自定义计算字段中有函数时的处理方式
          if (hasFn) {
            // 初始化 agg 字段
            if (isFirstAgg) {
              isFirstAgg = false;
              params.agg = {
                aggregateObjs: [],
              };
            }

            // 目前只能处理单函数 单参数 ,目前匹配第一个函数，第一个中括号的内容
            const reg = /\[(.+?)\]/g;
            const fields = item.mapping.match(reg);
            let field = '';
            if (fields.length) {
              field = fields[0];
            }

            params.agg.aggregateObjs.push({
              alias: item.mapping,
              field: field,
              aggregator: fnName,
            });
          } else {
            // 初始化 customFields 字段
            if (isFirstCustomFields) {
              isFirstCustomFields = false;
              params.customFields = [];
            }
            params.customFields.push({
              fieldsName: item.mapping,
              function: item.mapping,
            });
          }
        }
      });
      return params;
    },

    // 更新 api 请求参数（事件的key 以及value） 目前只适配xMiner的入参
    updateParams(obj) {
      if (!this.config.config.data) {
        return;
      }

      const { apis, datas } = this.config.config.data;

      datas.forEach((dataItem, dataIndex) => {
        const apiCode = dataItem[0].mapping;
        if (apiCode) {
          const apiObj = apis.find(a => String(a.apiCode) === String(apiCode));
          let { dataSourceType } = apiObj;
          dataSourceType = dataSourceType === 'xMinerPublicDatabase' ? 'xMiner' : dataSourceType;
          if (apiObj) {
            this[`updateParams${dataSourceType}`]({
              obj,
              dataItem,
              dataIndex,
              apiObj,
            });
          }
        }
      });
    },
    updateParamsxMiner({ obj, dataItem, dataIndex, apiObj }) {
      for (const key in obj) {
        if (obj[key] === undefined) {
          continue;
        }
        if (key === 'xTag_parms_type') {
          continue;
        }
        if (key.indexOf(EMIT_PARAMS_OBJ) <= -1) {
          continue;
        }
        if (!this.params[dataIndex]) {
          this.params[dataIndex] = {};
        }
        if (!this.params[dataIndex].params) {
          this.params[dataIndex].params = [];
        }

        // 参数值存放在EMIT_PARAMS_OBJ内部，不放在外部，实验性方案，触发参考slide-fitler组件
        const updateRes = this.updateParamsByWid({ [key]: obj[key] }, 'xMiner', this.params[dataIndex]);
        if (updateRes) {
          continue;
        }

        const paramObj = obj[key];
        const { eventKey } = paramObj;
        const paramKey = paramObj.key;
        const value = obj[paramKey];

        // 特殊情况 时间有可能有2个key
        const paramKey2 = paramObj.key_endTime;
        const value2 = obj[paramKey2];

        // 遍历config 查看配置的事件
        this.config.events.forEach(event => {
          if (event.type === 'in' && String(event.key) === String(eventKey)) {
            // value为空时不放入params
            const paramItem = {
              code: paramKey,
              value: value,
            };
            const { params } = this.params[dataIndex];
            const paramIndex = params.findIndex(p => String(p.code) === String(paramKey));
            if (paramIndex === -1) {
              if (!isEmpty(value)) {
                params.push(paramItem);
              }
            } else {
              if (isEmpty(value)) {
                params.splice(paramIndex, 1);
              } else {
                params.splice(paramIndex, 1, paramItem);
              }
            }

            // 特殊情况 时间有可能有2个key
            if (paramKey2 && value2) {
              const param2 = params.find(p => String(p.code) === String(paramKey2));
              if (param2) {
                param2.value = value2;
              } else {
                params.push({
                  code: paramKey2,
                  value: value2,
                });
              }
            } else {
              const endPram = params.find(p => String(p.code) === 'endTime');
              if (endPram) {
                const index = params.indexOf(endPram);
                params.splice(index, 1);
              }
            }
          }
        });
      }
    },
    updateParamsxTag({ obj, dataItem, dataIndex, apiObj }) {
      if (!this.params[dataIndex]) {
        this.params[dataIndex] = {};
      }
      if (!this.params[dataIndex].conditions) {
        this.params[dataIndex].conditions = [{}];
      }
      if (!this.params[dataIndex].sqlParams) {
        this.params[dataIndex].sqlParams = [];
      }

      for (const key in obj) {
        if (key.indexOf(EMIT_PARAMS_OBJ) <= -1) {
          continue;
        }

        // 参数值存放在EMIT_PARAMS_OBJ内部，不放在外部，实验性方案，触发参考slide-fitler组件
        const updateRes = this.updateParamsByWid({ [key]: obj[key] }, 'xTag', this.params[dataIndex]);
        if (updateRes) {
          continue;
        }

        const paramObj = obj[key];
        const { eventKey } = paramObj;
        const paramKey = paramObj.key;
        const value = obj[paramKey];

        // 遍历config 查看配置的事件
        let flag = false;
        this.config.events.forEach(event => {
          if (event.type === 'in' && String(event.key) === String(eventKey)) {
            this.updateParamsXTagSqlParams(this.params[dataIndex].sqlParams, obj, key);
            flag = true;
          }
        });
        if (!flag) {
          continue;
        }

        if (paramObj.type === 'range') {
          // 用空值对象清除当前params conditions
          this.checkConditionsType('range', dataIndex);
          this.checkConditionsType('terms', dataIndex);
          this.updateParamsXTagConditions('', 'range', paramKey, dataIndex);
          this.updateParamsXTagConditions('', 'terms', paramKey, dataIndex);
          this.updateParamsXTagConditionsRange({
            paramObj,
            paramKey,
            value,
            obj,
            dataIndex,
          });
        } else {
          this.checkConditionsType(paramObj.type, dataIndex);
          this.updateParamsXTagConditions(value, paramObj.type, paramKey, dataIndex);
        }
      }
    },
    updateParamsXTagSqlParams(target, obj, key) {
      const paramObj = obj[key];
      const paramKey = paramObj.key;
      const value = obj[paramKey];

      // 特殊情况 时间有可能有2个key
      const paramKey2 = paramObj.key_endTime;
      const value2 = obj[paramKey2];
      const paramItem = {
        key: paramKey,
        value: value,
      };
      const paramIndex = target.findIndex(p => String(p.key) === String(paramKey));

      // 不存在
      if (paramIndex === -1) {
        if (!isEmpty(value)) {
          target.push(paramItem);
        }
      } else {
        // 存在，但为空值，从target中移除
        if (isEmpty(value)) {
          target.splice(paramIndex, 1);
        } else {
          target.splice(paramIndex, 1, paramItem);
        }
      }

      // 特殊情况 时间有可能有2个key
      if (paramKey2) {
        const param2 = target.find(p => String(p.key) === String(paramKey2));
        if (param2) {
          param2.value = value2 ? value2 : param2.value;
        } else {
          if (value2) {
            target.push({
              key: paramKey2,
              value: value2,
            });
          }
        }
      } else {
        // 当且仅当时间筛选组件时间范围切换时间点场景才删除值
        const timeOption = paramObj.option;
        if (!timeOption) {
          return;
        }
        const endPram = target.find(p => String(p.key) === String(timeOption.key_endTime));
        if (endPram) {
          const index = target.indexOf(endPram);
          target.splice(index, 1);
        }
      }
    },
    updateParamsXTagConditions(value, type, paramKey, dataIndex) {
      let itemIndex;
      let item;

      // 找到同名的参数
      this.params[dataIndex].conditions[0][type].forEach((paramsConditionObj, index) => {
        const keys = Object.keys(paramsConditionObj);
        if (keys.includes(paramKey)) {
          item = paramsConditionObj;
          itemIndex = index;
        }
      });

      // 空值的情况 删除该过滤条件
      if (isEmpty(value)) {
        if (item) {
          this.params[dataIndex].conditions[0][type].splice(itemIndex, 1);
        }
      } else {
        // type 等于 terms  时，用 [] 包装 数据
        let _value = value;
        if (type === 'terms') {
          _value = [_value];
        }
        if (item) {
          item[paramKey] = _value;
        } else {
          this.params[dataIndex].conditions[0][type].push({ [paramKey]: _value });
        }
      }
    },
    updateParamsXTagConditionsRange({ paramObj, paramKey, value, obj, dataIndex }) {
      // 时间组件的结束时间
      const paramKeyEndTime = paramObj.key_endTime;
      const valueEndTime = obj[paramKeyEndTime];
      const type = valueEndTime ? 'range' : 'terms';
      const item = this.params[dataIndex].conditions[0][type].find(i => {
        const keys = Object.keys(i);
        return keys.includes(paramKey);
      });

      if (item) {
        if (valueEndTime) {
          item[paramKey] = { gte: value, lte: valueEndTime };
        } else {
          item[paramKey] = [value];
        }
      } else {
        if (valueEndTime) {
          this.params[dataIndex].conditions[0][type].push({
            [paramKey]: { gte: value, lte: valueEndTime },
          });
        } else {
          this.params[dataIndex].conditions[0][type].push({
            [paramKey]: [value],
          });
        }
      }
    },
    checkConditionsType(type, dataIndex) {
      if (!this.params[dataIndex].conditions[0][type]) {
        this.params[dataIndex].conditions[0][type] = [];
      }
    },
    getTooltip() {
      if (!this.params) {
        this.params = [];
      }

      // 目前只支持xMiner
      let params = this.params[0] || {};
      params = JSON.parse(JSON.stringify(params));
      const matchParams = this._getCommonParamsByEvents(this.config.events);
      params.params = this._addCommonParamsBySourceType('xMiner', params.params, matchParams);

      return {
        config: this.config.config.property.tooltip || {},
        vars: {},
        params: params,
        parentComp: this,
      };
    },

    // 从事件中提取公共参数数组
    _getCommonParamsByEvents(events) {
      const evtKeys = [];
      events.forEach(n => {
        if (n.type === 'in') {
          evtKeys.push(n.key);
        }
      });
      if (!evtKeys.length) {
        return [];
      }

      const { commonParams } = this;
      if (!commonParams || !Object.keys(commonParams).length) {
        return [];
      }

      const paramsMap = {};
      Object.entries(commonParams).map(([key]) => {
        const { evtKey, params } = commonParams[key];
        if (evtKeys.includes(evtKey)) {
          for (let i = 0; i < params.length; i++) {
            const n = params[i];
            paramsMap[n.key] = n;
          }
        }
      });
      const matchParams = Object.values(paramsMap);
      return matchParams;
    },

    /*
     * 将公共参数添加到已有参数数组中
     * xMiner 与 xtag 的params入参不同
     */
    _addCommonParamsBySourceType(dataSourceType, params, matchParams) {
      let _params;
      if (!params) {
        _params = [];
      } else {
        _params = params;
      }
      if (dataSourceType === 'xMiner') {
        _params = _params.filter(n => !n.isCommon);
        for (let i = 0; i < matchParams.length; i++) {
          const hasParam = _params.some(n => n.code === matchParams[i].key);
          if (!hasParam) {
            _params.push({
              code: matchParams[i].key,
              value: matchParams[i].value,
              isCommon: 1,
            });
          }
        }
      } else if (dataSourceType === 'xTag') {
        if (!matchParams.length) {
          return null;
        }

        let { conditions = [] } = params;
        const { sqlParams = [] } = params;
        conditions = conditions.filter(n => !n.isCommon);
        const item = {
          terms: [],
          isCommon: 1,
        };
        const { terms } = item;

        for (let i = 0; i < matchParams.length; i++) {
          const { key, value } = matchParams[i];

          // conditions 部分
          const hasParam = terms.some(n => {
            const keys = Object.keys(n);
            return keys.includes(key);
          });
          if (!hasParam) {
            terms.push({
              [key]: [value],
            });
          }
          conditions.push(item);
          params.conditions = conditions;

          // sqlParams 部分
          const hasSqlItem = sqlParams.some(n => n.key === key);
          if (!hasSqlItem) {
            sqlParams.push({
              key,
              value,
            });
          }
        }
        return null;
      } else {}
      return _params;
    },

  },
};
</script>
<style lang="less" scoped>
// 图表通用样式，主要有黑色loading 和 黑色 ‘没有数据’，样式名需一致
@import '../../common/chart.less';
.light {
  background-color: #fff !important;
}
.dark {
  background-color: #0d263a !important;
}
</style>
<style>
/* 全局loading样式修改 */

.chartMain .loading .el-loading-spinner .circular {
  width: 34px !important;
  height: 34px !important;
}

.chartMain .el-dialog__header {
  padding: 9px 20px 10px !important;
  background: #2a3352 !important;
  text-align: left;
  box-sizing: border-box;
  height: 50px;
  line-height: 32px;
}

.chartMain .el-dialog__title {
  text-align: left;
  font-size: 18px;
  line-height: 18px !important;
  color: #b2d3fe !important;
}

.chartMain .el-dialog__body {
  padding: 10px 20px 30px 20px;
  background: #1a2032 !important;
  line-height: 24px;
  font-size: 14px;
}

.chartMain .el-dialog__headerbtn .el-dialog__close {
  color: #b2d3fe !important;
}

.chartMain .dialog-footer {
  text-align: right;
  margin-bottom: -14px;
  margin-right: 4px;
}

.chartMain .el-dialog__headerbtn {
  top: 15px;
  font-size: 18px;
}
</style>
